The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
Changes 09
MANIFEST 04
META.yml 23
README 4050
inc/Module/AutoInstall.pm 215
inc/Module/Install/AutoInstall.pm 11
inc/Module/Install/Base.pm 11
inc/Module/Install/Can.pm 11
inc/Module/Install/Fetch.pm 11
inc/Module/Install/Include.pm 11
inc/Module/Install/Makefile.pm 26163
inc/Module/Install/Metadata.pm 49119
inc/Module/Install/Win32.pm 11
inc/Module/Install/WriteAll.pm 25
inc/Module/Install.pm 8697
lib/MooseX/Meta/TypeCoercion/Structured/Optional.pm 025
lib/MooseX/Meta/TypeConstraint/Structured/Optional.pm 021
lib/MooseX/Meta/TypeConstraint/Structured.pm 11
lib/MooseX/Types/Structured.pm 107157
t/04-map.t 021
t/bug-optional.t 043
21 files changed (This is a version diff) 321739
@@ -1,5 +1,14 @@
 Revision history for MooseX-Types-Structured
 
+0.21   26 March 2010
+        - Removed unneed module from test
+	- additional contributed documentation fixes
+
+0.20   04 February 2010
+        - Add a new Map type. (Ricardo SIGNES)
+        - Properly handle Optional[] types within Tuples and Dicts.
+          (Florian Ragwitz)
+
 0.19   06 November 2009
         - Require Devel::PartialDump 0.10 and change tests to expect the
           correct format of error.
@@ -12,7 +12,9 @@ inc/Module/Install/Metadata.pm
 inc/Module/Install/Win32.pm
 inc/Module/Install/WriteAll.pm
 lib/MooseX/Meta/TypeCoercion/Structured.pm
+lib/MooseX/Meta/TypeCoercion/Structured/Optional.pm
 lib/MooseX/Meta/TypeConstraint/Structured.pm
+lib/MooseX/Meta/TypeConstraint/Structured/Optional.pm
 lib/MooseX/Types/Structured.pm
 lib/MooseX/Types/Structured/OverflowHandler.pm
 Makefile.PL
@@ -26,6 +28,7 @@ t/01-basic.t
 t/02-tuple.t
 t/03-dict.t
 t/04-combined.t
+t/04-map.t
 t/05-advanced.t
 t/06-api.t
 t/07-coerce.t
@@ -34,3 +37,4 @@ t/09-optional.t
 t/10-recursion.t
 t/11-overflow.t
 t/12-error.t
+t/bug-optional.t
@@ -2,6 +2,7 @@
 abstract: 'Moose Type Constraint classes for Structured Types'
 author:
   - 'John Napiorkowski <jjnapiork@cpan.org>'
+  - 'John Napiorkowski <jjnapiork@cpan.org>'
 build_requires:
   ExtUtils::MakeMaker: 6.42
   Test::Exception: 0.27
@@ -9,7 +10,7 @@ build_requires:
 configure_requires:
   ExtUtils::MakeMaker: 6.42
 distribution_type: module
-generated_by: 'Module::Install version 0.91'
+generated_by: 'Module::Install version 0.95'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -27,4 +28,4 @@ requires:
   perl: 5.8.0
 resources:
   license: http://dev.perl.org/licenses/
-version: 0.19
+version: 0.21
@@ -5,7 +5,7 @@ SYNOPSIS
     The following is example usage for this module.
 
         package Person;
-        
+
         use Moose;
         use MooseX::Types::Moose qw(Str Int HashRef);
         use MooseX::Types::Structured qw(Dict Tuple Optional);
@@ -18,7 +18,7 @@ SYNOPSIS
                 middle => Optional[Str],
             ],
         );
-    
+
         ## description is a string field followed by a HashRef of tagged data.
         has description => (
           isa=>Tuple[
@@ -60,18 +60,18 @@ SYNOPSIS
 
         ## Value for 'name' not a HashRef
         Person->new( name => 'John' );
-    
+
         ## Value for 'name' has incorrect hash key and missing required keys
         Person->new( name => {
             first_name => 'John'
         });
-    
+
         ## Also incorrect keys
         Person->new( name => {
             first_name => 'John',
             age => 39,
         });
-    
+
         ## key 'middle' incorrect type, should be a Str not a ArrayRef
         Person->new( name => {
             first => 'Vanessa',
@@ -84,7 +84,7 @@ SYNOPSIS
 
         ## Should be an ArrayRef
         Person->new( description => 'Hello I am a String' );
-    
+
         ## First element must be a string not a HashRef.
         Person->new (description => [{
             tag1 => 'value1',
@@ -110,12 +110,12 @@ DESCRIPTION
 
   Comparing Parameterized types to Structured types
     Parameterized constraints are built into core Moose and you are probably
-    already familar with the type constraints 'HashRef' and 'ArrayRef'.
+    already familiar with the type constraints 'HashRef' and 'ArrayRef'.
     Structured types have similar functionality, so their syntax is likewise
     similar. For example, you could define a parameterized constraint like:
 
         subtype ArrayOfInts,
-         as Arrayref[Int];
+         as ArrayRef[Int];
 
     which would constrain a value to something like [1,2,3,...] and so on.
     On the other hand, a structured type constraint explicitly names all
@@ -172,7 +172,7 @@ DESCRIPTION
     This would validate the following:
 
         ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
-        ['World', 200, undef];    
+        ['World', 200, undef];
         ['World', 200];
 
     Structured constraints are not limited to arrays. You can define a
@@ -193,10 +193,10 @@ DESCRIPTION
 
         ## Incorrect keys
         {first => 'Christopher', last => 'Parsons'};
-    
+
         ## Too many keys
         {firstname => 'Christopher', lastname => 'Parsons', middlename => 'Allen'};
-    
+
         ## Not a HashRef
         ['Christopher', 'Parsons'];
 
@@ -243,15 +243,15 @@ DESCRIPTION
 
         package MyApp::MyStruct;
         use Moose;
-    
+
         ## lazy way to make a bunch of attributes
         has $_ for qw(full_name age_in_years);
-    
+
         package MyApp::MyClass;
         use Moose;
-    
-        has person => (isa => 'MyApp::MyStruct');           
-    
+
+        has person => (isa => 'MyApp::MyStruct');
+
         my $instance = MyApp::MyClass->new(
             person=>MyApp::MyStruct->new(
                 full_name => 'John',
@@ -265,13 +265,13 @@ DESCRIPTION
     Try:
 
         package MyApp::MyClass;
-    
+
         use Moose;
         use MyApp::MyStruct;
-    
+
         ## It's recommended your type declarations live in a separate class in order
         ## to promote reusability and clarity.  Inlined here for brevity.
-    
+
         use MooseX::Types::DateTime qw(DateTime);
         use MooseX::Types -declare [qw(MyStruct)];
         use MooseX::Types::Moose qw(Str Int);
@@ -284,7 +284,7 @@ DESCRIPTION
         ## Just a shorter version really.
         subtype MyStruct,
          as 'MyApp::MyStruct';
-    
+
         ## Add the coercions.
         coerce MyStruct,
          from Dict[
@@ -300,13 +300,13 @@ DESCRIPTION
          ], via {
             my $name = $_->{firstname} .' '. $_->{lastname};
             my $age = DateTime->now - $_->{dob};
-        
+
             MyApp::MyStruct->new(
                 full_name=>$name,
                 age_in_years=>$age->years,
             );
          };
-     
+
         has person => (isa=>MyStruct);
 
     This would allow you to instantiate with something like:
@@ -334,7 +334,7 @@ DESCRIPTION
 
         subtype Person,
          as Dict[name => Str];
-         
+
         subtype FriendlyPerson,
          as Person[
             name => Str,
@@ -357,13 +357,13 @@ DESCRIPTION
             name => Str,
             age => Int
         ];
-    
+
         subtype Fullname,
          as Dict[
             first => Str,
             last => Str
          ];
-    
+
         coerce Person,
          ## Coerce an object of a particular class
          from BlessedPersonObject, via {
@@ -372,7 +372,7 @@ DESCRIPTION
                 age=>$_->age,
             };
          },
-     
+
          ## Coerce from [$name, $age]
          from ArrayRef, via {
             +{
@@ -484,6 +484,16 @@ TYPE CONSTRAINTS
     The keys in %constraints follow the same rules as @constraints in the
     above section.
 
+  Map[ $key_constraint, $value_constraint ]
+    This defines a HashRef based constraint in which both the keys and
+    values are required to meet certain constraints. For example, to map
+    hostnames to IP addresses, you might say:
+
+      Map[ HostName, IPAddress ]
+
+    The type constraint would only be met if every key was a valid HostName
+    and every value was a valid IPAddress.
+
   Optional[$constraint]
     This is primarily a helper constraint for Dict and Tuple type
     constraints. What this allows is for you to assert that a given type
@@ -539,7 +549,7 @@ EXPORTABLE SUBROUTINES
     you can use the "slurpy" method against a type constraint. For example:
 
         use MooseX::Types::Structured qw(Tuple slurpy);
-    
+
         subtype AllowTailingArgs,
          as Tuple[
            Int,
@@ -595,25 +605,25 @@ EXAMPLES
     structured types mixed which other MooseX::Types libraries.
 
         package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
-    
+
         use Moose;
         use DateTime;
-    
+
         use MooseX::Types::Structured qw(Dict Tuple);
         use MooseX::Types::DateTime qw(DateTime);
         use MooseX::Types::Moose qw(Int Str Object);
         use MooseX::Types -declare => [qw(Name Age Person)];
-     
+
         subtype Person,
          as Dict[
             name=>Str,
             age=>Int,
          ];
-    
+
         coerce Person,
          from Dict[
-            first=>Str, 
-            last=>Str, 
+            first=>Str,
+            last=>Str,
             years=>Int,
          ], via { +{
             name => "$_->{first} $_->{last}",
@@ -621,9 +631,9 @@ EXAMPLES
          }},
          from Dict[
             fullname=>Dict[
-                    last=>Str, 
+                    last=>Str,
                     first=>Str,
-            ], 
+            ],
             dob=>DateTime,
          ],
          ## DateTime needs to be inside of single quotes here to disambiguate the
@@ -633,7 +643,7 @@ EXAMPLES
             name => "$_->{fullname}{first} $_->{fullname}{last}",
             age => ($_->{dob} - 'DateTime'->now)->years,
          }};
-     
+
         has person => (is=>'rw', isa=>Person, coerce=>1);
 
     And now you can instantiate with all the following:
@@ -641,10 +651,10 @@ EXAMPLES
         __PACKAGE__->new(
             person=>{
                 name=>'John Napiorkowski',
-                age=>39,            
+                age=>39,
             },
         );
-        
+
         __PACKAGE__->new(
             person=>{
                 first=>'John',
@@ -652,7 +662,7 @@ EXAMPLES
                 years=>39,
             },
         );
-    
+
         __PACKAGE__->new(
             person=>{
                 fullname => {
@@ -663,7 +673,7 @@ EXAMPLES
                     year=>1969,
                     month=>2,
                     day=>13
-                ),            
+                ),
             },
         );
 
@@ -672,7 +672,20 @@ sub _load {
 sub _load_cpan {
     return if $CPAN::VERSION and $CPAN::Config and not @_;
     require CPAN;
-    if ( $CPAN::HandleConfig::VERSION ) {
+
+    # CPAN-1.82+ adds CPAN::Config::AUTOLOAD to redirect to
+    #    CPAN::HandleConfig->load. CPAN reports that the redirection
+    #    is deprecated in a warning printed at the user.
+
+    # CPAN-1.81 expects CPAN::HandleConfig->load, does not have
+    #   $CPAN::HandleConfig::VERSION but cannot handle
+    #   CPAN::Config->load
+
+    # Which "versions expect CPAN::Config->load?
+
+    if ( $CPAN::HandleConfig::VERSION
+        || CPAN::HandleConfig->can('load')
+    ) {
         # Newer versions of CPAN have a HandleConfig module
         CPAN::HandleConfig->load;
     } else {
@@ -802,4 +815,4 @@ END_MAKE
 
 __END__
 
-#line 1056
+#line 1069
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.91';
+	$VERSION = '0.95';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -4,7 +4,7 @@ package Module::Install::Base;
 use strict 'vars';
 use vars qw{$VERSION};
 BEGIN {
-	$VERSION = '0.91';
+	$VERSION = '0.95';
 }
 
 # Suspend handler for "redefined" warnings
@@ -9,7 +9,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.91';
+	$VERSION = '0.95';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.91';
+	$VERSION = '0.95';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.91';
+	$VERSION = '0.95';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -7,7 +7,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.91';
+	$VERSION = '0.95';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -25,8 +25,8 @@ sub prompt {
 		die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
 	}
 
-	# In automated testing, always use defaults
-	if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) {
+	# In automated testing or non-interactive session, always use defaults
+	if ( ($ENV{AUTOMATED_TESTING} or -! -t STDIN) and ! $ENV{PERL_MM_USE_DEFAULT} ) {
 		local $ENV{PERL_MM_USE_DEFAULT} = 1;
 		goto &ExtUtils::MakeMaker::prompt;
 	} else {
@@ -34,21 +34,110 @@ sub prompt {
 	}
 }
 
+# Store a cleaned up version of the MakeMaker version,
+# since we need to behave differently in a variety of
+# ways based on the MM version.
+my $makemaker = eval $ExtUtils::MakeMaker::VERSION;
+
+# If we are passed a param, do a "newer than" comparison.
+# Otherwise, just return the MakeMaker version.
+sub makemaker {
+	( @_ < 2 or $makemaker >= eval($_[1]) ) ? $makemaker : 0
+}
+
+# Ripped from ExtUtils::MakeMaker 6.56, and slightly modified
+# as we only need to know here whether the attribute is an array
+# or a hash or something else (which may or may not be appendable).
+my %makemaker_argtype = (
+ C                  => 'ARRAY',
+ CONFIG             => 'ARRAY',
+# CONFIGURE          => 'CODE', # ignore
+ DIR                => 'ARRAY',
+ DL_FUNCS           => 'HASH',
+ DL_VARS            => 'ARRAY',
+ EXCLUDE_EXT        => 'ARRAY',
+ EXE_FILES          => 'ARRAY',
+ FUNCLIST           => 'ARRAY',
+ H                  => 'ARRAY',
+ IMPORTS            => 'HASH',
+ INCLUDE_EXT        => 'ARRAY',
+ LIBS               => 'ARRAY', # ignore ''
+ MAN1PODS           => 'HASH',
+ MAN3PODS           => 'HASH',
+ META_ADD           => 'HASH',
+ META_MERGE         => 'HASH',
+ PL_FILES           => 'HASH',
+ PM                 => 'HASH',
+ PMLIBDIRS          => 'ARRAY',
+ PMLIBPARENTDIRS    => 'ARRAY',
+ PREREQ_PM          => 'HASH',
+ CONFIGURE_REQUIRES => 'HASH',
+ SKIP               => 'ARRAY',
+ TYPEMAPS           => 'ARRAY',
+ XS                 => 'HASH',
+# VERSION            => ['version',''],  # ignore
+# _KEEP_AFTER_FLUSH  => '',
+
+ clean      => 'HASH',
+ depend     => 'HASH',
+ dist       => 'HASH',
+ dynamic_lib=> 'HASH',
+ linkext    => 'HASH',
+ macro      => 'HASH',
+ postamble  => 'HASH',
+ realclean  => 'HASH',
+ test       => 'HASH',
+ tool_autosplit => 'HASH',
+
+ # special cases where you can use makemaker_append
+ CCFLAGS   => 'APPENDABLE',
+ DEFINE    => 'APPENDABLE',
+ INC       => 'APPENDABLE',
+ LDDLFLAGS => 'APPENDABLE',
+ LDFROM    => 'APPENDABLE',
+);
+
 sub makemaker_args {
-	my $self = shift;
+	my ($self, %new_args) = @_;
 	my $args = ( $self->{makemaker_args} ||= {} );
-	%$args = ( %$args, @_ );
+	foreach my $key (keys %new_args) {
+		if ($makemaker_argtype{$key} eq 'ARRAY') {
+			$args->{$key} = [] unless defined $args->{$key};
+			unless (ref $args->{$key} eq 'ARRAY') {
+				$args->{$key} = [$args->{$key}]
+			}
+			push @{$args->{$key}},
+				ref $new_args{$key} eq 'ARRAY'
+					? @{$new_args{$key}}
+					: $new_args{$key};
+		}
+		elsif ($makemaker_argtype{$key} eq 'HASH') {
+			$args->{$key} = {} unless defined $args->{$key};
+			foreach my $skey (keys %{ $new_args{$key} }) {
+				$args->{$key}{$skey} = $new_args{$key}{$skey};
+			}
+		}
+		elsif ($makemaker_argtype{$key} eq 'APPENDABLE') {
+			$self->makemaker_append($key => $new_args{$key});
+		}
+		else {
+			if (defined $args->{$key}) {
+				warn qq{MakeMaker attribute "$key" is overriden; use "makemaker_append" to append values\n};
+			}
+			$args->{$key} = $new_args{$key};
+		}
+	}
 	return $args;
 }
 
 # For mm args that take multiple space-seperated args,
 # append an argument to the current list.
 sub makemaker_append {
-	my $self = sShift;
+	my $self = shift;
 	my $name = shift;
 	my $args = $self->makemaker_args;
-	$args->{name} = defined $args->{$name}
-		? join( ' ', $args->{name}, @_ )
+	$args->{$name} = defined $args->{$name}
+		? join( ' ', $args->{$name}, @_ )
 		: join( ' ', @_ );
 }
 
@@ -107,6 +196,9 @@ sub tests_recursive {
 	%test_dir = ();
 	require File::Find;
 	File::Find::find( \&_wanted_t, $dir );
+	if ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) {
+		File::Find::find( \&_wanted_t, 'xt' );
+	}
 	$self->tests( join ' ', map { "$_/*.t" } sort keys %test_dir );
 }
 
@@ -130,12 +222,13 @@ sub write {
 		# an underscore, even though its own version may contain one!
 		# Hence the funny regexp to get rid of it.  See RT #35800
 		# for details.
-		$self->build_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/ );
-		$self->configure_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/ );
+		my $v = $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/;
+		$self->build_requires(     'ExtUtils::MakeMaker' => $v );
+		$self->configure_requires( 'ExtUtils::MakeMaker' => $v );
 	} else {
 		# Allow legacy-compatibility with 5.005 by depending on the
 		# most recent EU:MM that supported 5.005.
-		$self->build_requires( 'ExtUtils::MakeMaker' => 6.42 );
+		$self->build_requires(     'ExtUtils::MakeMaker' => 6.42 );
 		$self->configure_requires( 'ExtUtils::MakeMaker' => 6.42 );
 	}
 
@@ -143,59 +236,103 @@ sub write {
 	my $args = $self->makemaker_args;
 	$args->{DISTNAME} = $self->name;
 	$args->{NAME}     = $self->module_name || $self->name;
-	$args->{VERSION}  = $self->version;
 	$args->{NAME}     =~ s/-/::/g;
+	$args->{VERSION}  = $self->version or die <<'EOT';
+ERROR: Can't determine distribution version. Please specify it
+explicitly via 'version' in Makefile.PL, or set a valid $VERSION
+in a module, and provide its file path via 'version_from' (or
+'all_from' if you prefer) in Makefile.PL.
+EOT
+
+	$DB::single = 1;
 	if ( $self->tests ) {
-		$args->{test} = { TESTS => $self->tests };
+		my @tests = split ' ', $self->tests;
+		my %seen;
+		$args->{test} = {
+			TESTS => (join ' ', grep {!$seen{$_}++} @tests),
+		};
+	} elsif ( -d 'xt' and ($Module::Install::AUTHOR or $ENV{RELEASE_TESTING}) ) {
+		$args->{test} = {
+			TESTS => join( ' ', map { "$_/*.t" } grep { -d $_ } qw{ t xt } ),
+		};
 	}
 	if ( $] >= 5.005 ) {
 		$args->{ABSTRACT} = $self->abstract;
-		$args->{AUTHOR}   = $self->author;
+		$args->{AUTHOR}   = join ', ', @{$self->author || []};
 	}
-	if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) {
-		$args->{NO_META} = 1;
+	if ( $self->makemaker(6.10) ) {
+		$args->{NO_META}   = 1;
+		#$args->{NO_MYMETA} = 1;
 	}
-	if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) {
+	if ( $self->makemaker(6.17) and $self->sign ) {
 		$args->{SIGN} = 1;
 	}
 	unless ( $self->is_admin ) {
 		delete $args->{SIGN};
 	}
+	if ( $self->makemaker(6.31) and $self->license ) {
+		$args->{LICENSE} = $self->license;
+	}
 
-	# Merge both kinds of requires into prereq_pm
 	my $prereq = ($args->{PREREQ_PM} ||= {});
 	%$prereq = ( %$prereq,
-		map { @$_ }
+		map { @$_ } # flatten [module => version]
 		map { @$_ }
 		grep $_,
-		($self->configure_requires, $self->build_requires, $self->requires)
+		($self->requires)
 	);
 
 	# Remove any reference to perl, PREREQ_PM doesn't support it
 	delete $args->{PREREQ_PM}->{perl};
 
-	# merge both kinds of requires into prereq_pm
+	# Merge both kinds of requires into BUILD_REQUIRES
+	my $build_prereq = ($args->{BUILD_REQUIRES} ||= {});
+	%$build_prereq = ( %$build_prereq,
+		map { @$_ } # flatten [module => version]
+		map { @$_ }
+		grep $_,
+		($self->configure_requires, $self->build_requires)
+	);
+
+	# Remove any reference to perl, BUILD_REQUIRES doesn't support it
+	delete $args->{BUILD_REQUIRES}->{perl};
+
+	# Delete bundled dists from prereq_pm
 	my $subdirs = ($args->{DIR} ||= []);
 	if ($self->bundles) {
 		foreach my $bundle (@{ $self->bundles }) {
 			my ($file, $dir) = @$bundle;
 			push @$subdirs, $dir if -d $dir;
-			delete $prereq->{$file};
+			delete $build_prereq->{$file}; #Delete from build prereqs only
 		}
 	}
 
+	unless ( $self->makemaker('6.55_03') ) {
+		%$prereq = (%$prereq,%$build_prereq);
+		delete $args->{BUILD_REQUIRES};
+	}
+
 	if ( my $perl_version = $self->perl_version ) {
 		eval "use $perl_version; 1"
 			or die "ERROR: perl: Version $] is installed, "
 			. "but we need version >= $perl_version";
+
+		if ( $self->makemaker(6.48) ) {
+			$args->{MIN_PERL_VERSION} = $perl_version;
+		}
 	}
 
-	$args->{INSTALLDIRS} = $self->installdirs;
+	if ($self->installdirs) {
+		warn qq{old INSTALLDIRS (probably set by makemaker_args) is overriden by installdirs\n} if $args->{INSTALLDIRS};
+		$args->{INSTALLDIRS} = $self->installdirs;
+	}
 
-	my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args;
+	my %args = map {
+		( $_ => $args->{$_} ) } grep {defined($args->{$_} )
+	} keys %$args;
 
 	my $user_preop = delete $args{dist}->{PREOP};
-	if (my $preop = $self->admin->preop($user_preop)) {
+	if ( my $preop = $self->admin->preop($user_preop) ) {
 		foreach my $key ( keys %$preop ) {
 			$args{dist}->{$key} = $preop->{$key};
 		}
@@ -265,4 +402,4 @@ sub postamble {
 
 __END__
 
-#line 394
+#line 531
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.91';
+	$VERSION = '0.95';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -19,7 +19,6 @@ my @scalar_keys = qw{
 	name
 	module_name
 	abstract
-	author
 	version
 	distribution_type
 	tests
@@ -43,8 +42,11 @@ my @resource_keys = qw{
 
 my @array_keys = qw{
 	keywords
+	author
 };
 
+*authors = \&author;
+
 sub Meta              { shift          }
 sub Meta_BooleanKeys  { @boolean_keys  }
 sub Meta_ScalarKeys   { @scalar_keys   }
@@ -230,6 +232,8 @@ sub all_from {
 		die("The path '$file' does not exist, or is not a file");
 	}
 
+	$self->{values}{all_from} = $file;
+
 	# Some methods pull from POD instead of code.
 	# If there is a matching .pod, use that instead
 	my $pod = $file;
@@ -240,7 +244,7 @@ sub all_from {
 	$self->name_from($file)         unless $self->name;
 	$self->version_from($file)      unless $self->version;
 	$self->perl_version_from($file) unless $self->perl_version;
-	$self->author_from($pod)        unless $self->author;
+	$self->author_from($pod)        unless @{$self->author || []};
 	$self->license_from($pod)       unless $self->license;
 	$self->abstract_from($pod)      unless $self->abstract;
 
@@ -385,11 +389,10 @@ sub name_from {
 	}
 }
 
-sub perl_version_from {
-	my $self = shift;
+sub _extract_perl_version {
 	if (
-		Module::Install::_read($_[0]) =~ m/
-		^
+		$_[0] =~ m/
+		^\s*
 		(?:use|require) \s*
 		v?
 		([\d_\.]+)
@@ -398,6 +401,16 @@ sub perl_version_from {
 	) {
 		my $perl_version = $1;
 		$perl_version =~ s{_}{}g;
+		return $perl_version;
+	} else {
+		return;
+	}
+}
+
+sub perl_version_from {
+	my $self = shift;
+	my $perl_version=_extract_perl_version(Module::Install::_read($_[0]));
+	if ($perl_version) {
 		$self->perl_version($perl_version);
 	} else {
 		warn "Cannot determine perl version info from $_[0]\n";
@@ -417,59 +430,116 @@ sub author_from {
 		([^\n]*)
 	/ixms) {
 		my $author = $1 || $2;
-		$author =~ s{E<lt>}{<}g;
-		$author =~ s{E<gt>}{>}g;
+
+		# XXX: ugly but should work anyway...
+		if (eval "require Pod::Escapes; 1") {
+			# Pod::Escapes has a mapping table.
+			# It's in core of perl >= 5.9.3, and should be installed
+			# as one of the Pod::Simple's prereqs, which is a prereq
+			# of Pod::Text 3.x (see also below).
+			$author =~ s{ E<( (\d+) | ([A-Za-z]+) )> }
+			{
+				defined $2
+				? chr($2)
+				: defined $Pod::Escapes::Name2character_number{$1}
+				? chr($Pod::Escapes::Name2character_number{$1})
+				: do {
+					warn "Unknown escape: E<$1>";
+					"E<$1>";
+				};
+			}gex;
+		}
+		elsif (eval "require Pod::Text; 1" && $Pod::Text::VERSION < 3) {
+			# Pod::Text < 3.0 has yet another mapping table,
+			# though the table name of 2.x and 1.x are different.
+			# (1.x is in core of Perl < 5.6, 2.x is in core of
+			# Perl < 5.9.3)
+			my $mapping = ($Pod::Text::VERSION < 2)
+				? \%Pod::Text::HTML_Escapes
+				: \%Pod::Text::ESCAPES;
+			$author =~ s{ E<( (\d+) | ([A-Za-z]+) )> }
+			{
+				defined $2
+				? chr($2)
+				: defined $mapping->{$1}
+				? $mapping->{$1}
+				: do {
+					warn "Unknown escape: E<$1>";
+					"E<$1>";
+				};
+			}gex;
+		}
+		else {
+			$author =~ s{E<lt>}{<}g;
+			$author =~ s{E<gt>}{>}g;
+		}
 		$self->author($author);
 	} else {
 		warn "Cannot determine author info from $_[0]\n";
 	}
 }
 
-sub license_from {
-	my $self = shift;
-	if (
-		Module::Install::_read($_[0]) =~ m/
-		(
-			=head \d \s+
-			(?:licen[cs]e|licensing|copyright|legal)\b
-			.*?
-		)
-		(=head\\d.*|=cut.*|)
-		\z
-	/ixms ) {
-		my $license_text = $1;
-		my @phrases      = (
-			'under the same (?:terms|license) as (?:perl|the perl programming language) itself' => 'perl', 1,
-			'GNU general public license'         => 'gpl',         1,
-			'GNU public license'                 => 'gpl',         1,
-			'GNU lesser general public license'  => 'lgpl',        1,
-			'GNU lesser public license'          => 'lgpl',        1,
-			'GNU library general public license' => 'lgpl',        1,
-			'GNU library public license'         => 'lgpl',        1,
-			'BSD license'                        => 'bsd',         1,
-			'Artistic license'                   => 'artistic',    1,
-			'GPL'                                => 'gpl',         1,
-			'LGPL'                               => 'lgpl',        1,
-			'BSD'                                => 'bsd',         1,
-			'Artistic'                           => 'artistic',    1,
-			'MIT'                                => 'mit',         1,
-			'proprietary'                        => 'proprietary', 0,
-		);
-		while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
-			$pattern =~ s{\s+}{\\s+}g;
-			if ( $license_text =~ /\b$pattern\b/i ) {
-				$self->license($license);
-				return 1;
-			}
+sub _extract_license {
+	my $pod = shift;
+	my $matched;
+	return __extract_license(
+		($matched) = $pod =~ m/
+			(=head \d \s+ (?:licen[cs]e|licensing)\b.*?)
+			(=head \d.*|=cut.*|)\z
+		/ixms
+	) || __extract_license(
+		($matched) = $pod =~ m/
+			(=head \d \s+ (?:copyrights?|legal)\b.*?)
+			(=head \d.*|=cut.*|)\z
+		/ixms
+	);
+}
+
+sub __extract_license {
+	my $license_text = shift or return;
+	my @phrases      = (
+		'under the same (?:terms|license) as (?:perl|the perl programming language)' => 'perl', 1,
+		'under the terms of (?:perl|the perl programming language) itself' => 'perl', 1,
+		'Artistic and GPL'                   => 'perl',        1,
+		'GNU general public license'         => 'gpl',         1,
+		'GNU public license'                 => 'gpl',         1,
+		'GNU lesser general public license'  => 'lgpl',        1,
+		'GNU lesser public license'          => 'lgpl',        1,
+		'GNU library general public license' => 'lgpl',        1,
+		'GNU library public license'         => 'lgpl',        1,
+		'BSD license'                        => 'bsd',         1,
+		'Artistic license'                   => 'artistic',    1,
+		'GPL'                                => 'gpl',         1,
+		'LGPL'                               => 'lgpl',        1,
+		'BSD'                                => 'bsd',         1,
+		'Artistic'                           => 'artistic',    1,
+		'MIT'                                => 'mit',         1,
+		'proprietary'                        => 'proprietary', 0,
+	);
+	while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
+		$pattern =~ s#\s+#\\s+#gs;
+		if ( $license_text =~ /\b$pattern\b/i ) {
+			return $license;
 		}
 	}
+}
 
-	warn "Cannot determine license info from $_[0]\n";
-	return 'unknown';
+sub license_from {
+	my $self = shift;
+	if (my $license=_extract_license(Module::Install::_read($_[0]))) {
+		$self->license($license);
+	} else {
+		warn "Cannot determine license info from $_[0]\n";
+		return 'unknown';
+	}
 }
 
 sub _extract_bugtracker {
-	my @links   = $_[0] =~ m#L<(\Qhttp://rt.cpan.org/\E[^>]+)>#g;
+	my @links   = $_[0] =~ m#L<(
+	 \Qhttp://rt.cpan.org/\E[^>]+|
+	 \Qhttp://github.com/\E[\w_]+/[\w_]+/issues|
+	 \Qhttp://code.google.com/p/\E[\w_\-]+/issues/list
+	 )>#gx;
 	my %links;
 	@links{@links}=();
 	@links=keys %links;
@@ -485,7 +555,7 @@ sub bugtracker_from {
 		return 0;
 	}
 	if ( @links > 1 ) {
-		warn "Found more than on rt.cpan.org link in $_[0]\n";
+		warn "Found more than one bugtracker link in $_[0]\n";
 		return 0;
 	}
 
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.91';
+	$VERSION = '0.95';
 	@ISA     = 'Module::Install::Base';
 	$ISCORE  = 1;
 }
@@ -6,7 +6,7 @@ use Module::Install::Base ();
 
 use vars qw{$VERSION @ISA $ISCORE};
 BEGIN {
-	$VERSION = '0.91';;
+	$VERSION = '0.95';;
 	@ISA     = qw{Module::Install::Base};
 	$ISCORE  = 1;
 }
@@ -26,7 +26,10 @@ sub WriteAll {
 
 	$self->check_nmake if $args{check_nmake};
 	unless ( $self->makemaker_args->{PL_FILES} ) {
-		$self->makemaker_args( PL_FILES => {} );
+		# XXX: This still may be a bit over-defensive...
+		unless ($self->makemaker(6.25)) {
+			$self->makemaker_args( PL_FILES => {} ) if -f 'Build.PL';
+		}
 	}
 
 	# Until ExtUtils::MakeMaker support MYMETA.yml, make sure
@@ -19,6 +19,10 @@ package Module::Install;
 
 use 5.005;
 use strict 'vars';
+use Cwd        ();
+use File::Find ();
+use File::Path ();
+use FindBin;
 
 use vars qw{$VERSION $MAIN};
 BEGIN {
@@ -28,7 +32,7 @@ BEGIN {
 	# This is not enforced yet, but will be some time in the next few
 	# releases once we can make sure it won't clash with custom
 	# Module::Install extensions.
-	$VERSION = '0.91';
+	$VERSION = '0.95';
 
 	# Storage for the pseudo-singleton
 	$MAIN    = undef;
@@ -38,18 +42,25 @@ BEGIN {
 
 }
 
+sub import {
+	my $class = shift;
+	my $self  = $class->new(@_);
+	my $who   = $self->_caller;
 
-
-
-
-# Whether or not inc::Module::Install is actually loaded, the
-# $INC{inc/Module/Install.pm} is what will still get set as long as
-# the caller loaded module this in the documented manner.
-# If not set, the caller may NOT have loaded the bundled version, and thus
-# they may not have a MI version that works with the Makefile.PL. This would
-# result in false errors or unexpected behaviour. And we don't want that.
-my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
-unless ( $INC{$file} ) { die <<"END_DIE" }
+	#-------------------------------------------------------------
+	# all of the following checks should be included in import(),
+	# to allow "eval 'require Module::Install; 1' to test
+	# installation of Module::Install. (RT #51267)
+	#-------------------------------------------------------------
+
+	# Whether or not inc::Module::Install is actually loaded, the
+	# $INC{inc/Module/Install.pm} is what will still get set as long as
+	# the caller loaded module this in the documented manner.
+	# If not set, the caller may NOT have loaded the bundled version, and thus
+	# they may not have a MI version that works with the Makefile.PL. This would
+	# result in false errors or unexpected behaviour. And we don't want that.
+	my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
+	unless ( $INC{$file} ) { die <<"END_DIE" }
 
 Please invoke ${\__PACKAGE__} with:
 
@@ -61,26 +72,28 @@ not:
 
 END_DIE
 
-
-
-
-
-# If the script that is loading Module::Install is from the future,
-# then make will detect this and cause it to re-run over and over
-# again. This is bad. Rather than taking action to touch it (which
-# is unreliable on some platforms and requires write permissions)
-# for now we should catch this and refuse to run.
-if ( -f $0 ) {
-	my $s = (stat($0))[9];
-
-	# If the modification time is only slightly in the future,
-	# sleep briefly to remove the problem.
-	my $a = $s - time;
-	if ( $a > 0 and $a < 5 ) { sleep 5 }
-
-	# Too far in the future, throw an error.
-	my $t = time;
-	if ( $s > $t ) { die <<"END_DIE" }
+	# This reportedly fixes a rare Win32 UTC file time issue, but
+	# as this is a non-cross-platform XS module not in the core,
+	# we shouldn't really depend on it. See RT #24194 for detail.
+	# (Also, this module only supports Perl 5.6 and above).
+	eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006;
+
+	# If the script that is loading Module::Install is from the future,
+	# then make will detect this and cause it to re-run over and over
+	# again. This is bad. Rather than taking action to touch it (which
+	# is unreliable on some platforms and requires write permissions)
+	# for now we should catch this and refuse to run.
+	if ( -f $0 ) {
+		my $s = (stat($0))[9];
+
+		# If the modification time is only slightly in the future,
+		# sleep briefly to remove the problem.
+		my $a = $s - time;
+		if ( $a > 0 and $a < 5 ) { sleep 5 }
+
+		# Too far in the future, throw an error.
+		my $t = time;
+		if ( $s > $t ) { die <<"END_DIE" }
 
 Your installer $0 has a modification time in the future ($s > $t).
 
@@ -89,15 +102,12 @@ This is known to create infinite loops in make.
 Please correct this, then run $0 again.
 
 END_DIE
-}
-
-
-
+	}
 
 
-# Build.PL was formerly supported, but no longer is due to excessive
-# difficulty in implementing every single feature twice.
-if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" }
+	# Build.PL was formerly supported, but no longer is due to excessive
+	# difficulty in implementing every single feature twice.
+	if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" }
 
 Module::Install no longer supports Build.PL.
 
@@ -107,23 +117,36 @@ Please remove all Build.PL files and only use the Makefile.PL installer.
 
 END_DIE
 
+	#-------------------------------------------------------------
 
+	# To save some more typing in Module::Install installers, every...
+	# use inc::Module::Install
+	# ...also acts as an implicit use strict.
+	$^H |= strict::bits(qw(refs subs vars));
 
+	#-------------------------------------------------------------
 
+	unless ( -f $self->{file} ) {
+		require "$self->{path}/$self->{dispatch}.pm";
+		File::Path::mkpath("$self->{prefix}/$self->{author}");
+		$self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
+		$self->{admin}->init;
+		@_ = ($class, _self => $self);
+		goto &{"$self->{name}::import"};
+	}
 
-# To save some more typing in Module::Install installers, every...
-# use inc::Module::Install
-# ...also acts as an implicit use strict.
-$^H |= strict::bits(qw(refs subs vars));
-
-
+	*{"${who}::AUTOLOAD"} = $self->autoload;
+	$self->preload;
 
+	# Unregister loader and worker packages so subdirs can use them again
+	delete $INC{"$self->{file}"};
+	delete $INC{"$self->{path}.pm"};
 
+	# Save to the singleton
+	$MAIN = $self;
 
-use Cwd        ();
-use File::Find ();
-use File::Path ();
-use FindBin;
+	return 1;
+}
 
 sub autoload {
 	my $self = shift;
@@ -152,33 +175,6 @@ sub autoload {
 	};
 }
 
-sub import {
-	my $class = shift;
-	my $self  = $class->new(@_);
-	my $who   = $self->_caller;
-
-	unless ( -f $self->{file} ) {
-		require "$self->{path}/$self->{dispatch}.pm";
-		File::Path::mkpath("$self->{prefix}/$self->{author}");
-		$self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
-		$self->{admin}->init;
-		@_ = ($class, _self => $self);
-		goto &{"$self->{name}::import"};
-	}
-
-	*{"${who}::AUTOLOAD"} = $self->autoload;
-	$self->preload;
-
-	# Unregister loader and worker packages so subdirs can use them again
-	delete $INC{"$self->{file}"};
-	delete $INC{"$self->{path}.pm"};
-
-	# Save to the singleton
-	$MAIN = $self;
-
-	return 1;
-}
-
 sub preload {
 	my $self = shift;
 	unless ( $self->{extensions} ) {
@@ -348,17 +344,24 @@ sub _caller {
 	return $call;
 }
 
+# Done in evals to avoid confusing Perl::MinimumVersion
+eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@;
 sub _read {
 	local *FH;
-	if ( $] >= 5.006 ) {
-		open( FH, '<', $_[0] ) or die "open($_[0]): $!";
-	} else {
-		open( FH, "< $_[0]"  ) or die "open($_[0]): $!";
-	}
+	open( FH, '<', $_[0] ) or die "open($_[0]): $!";
 	my $string = do { local $/; <FH> };
 	close FH or die "close($_[0]): $!";
 	return $string;
 }
+END_NEW
+sub _read {
+	local *FH;
+	open( FH, "< $_[0]"  ) or die "open($_[0]): $!";
+	my $string = do { local $/; <FH> };
+	close FH or die "close($_[0]): $!";
+	return $string;
+}
+END_OLD
 
 sub _readperl {
 	my $string = Module::Install::_read($_[0]);
@@ -379,18 +382,26 @@ sub _readpod {
 	return $string;
 }
 
+# Done in evals to avoid confusing Perl::MinimumVersion
+eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@;
 sub _write {
 	local *FH;
-	if ( $] >= 5.006 ) {
-		open( FH, '>', $_[0] ) or die "open($_[0]): $!";
-	} else {
-		open( FH, "> $_[0]"  ) or die "open($_[0]): $!";
+	open( FH, '>', $_[0] ) or die "open($_[0]): $!";
+	foreach ( 1 .. $#_ ) {
+		print FH $_[$_] or die "print($_[0]): $!";
 	}
+	close FH or die "close($_[0]): $!";
+}
+END_NEW
+sub _write {
+	local *FH;
+	open( FH, "> $_[0]"  ) or die "open($_[0]): $!";
 	foreach ( 1 .. $#_ ) {
 		print FH $_[$_] or die "print($_[0]): $!";
 	}
 	close FH or die "close($_[0]): $!";
 }
+END_OLD
 
 # _version is for processing module versions (eg, 1.03_05) not
 # Perl versions (eg, 5.8.1).
@@ -427,4 +438,4 @@ sub _CLASS ($) {
 
 1;
 
-# Copyright 2008 - 2009 Adam Kennedy.
+# Copyright 2008 - 2010 Adam Kennedy.
@@ -0,0 +1,25 @@
+package MooseX::Meta::TypeCoercion::Structured::Optional;
+
+use Moose;
+extends 'Moose::Meta::TypeCoercion';
+
+sub compile_type_coercion {
+    my ($self) = @_;
+    my $constraint = $self->type_constraint->type_parameter;
+
+    $self->_compiled_type_coercion(sub {
+        my ($value) = @_;
+        return unless $constraint->has_coercion;
+        return $constraint->coerce($value);
+    });
+}
+
+sub has_coercion_for_type { 0 }
+
+sub add_type_coercions {
+    Moose->throw_error("Cannot add additional type coercions to Optional types");
+}
+
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+
+1;
@@ -0,0 +1,21 @@
+package MooseX::Meta::TypeConstraint::Structured::Optional;
+
+use Moose;
+use MooseX::Meta::TypeCoercion::Structured::Optional;
+
+extends 'Moose::Meta::TypeConstraint::Parameterizable';
+
+around parameterize => sub {
+    my $orig = shift;
+    my $self = shift;
+
+    my $ret = $self->$orig(@_);
+
+    $ret->coercion(MooseX::Meta::TypeCoercion::Structured::Optional->new(type_constraint => $ret));
+
+    return $ret;
+};
+
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+
+1;
@@ -267,7 +267,7 @@ sub is_subtype_of {
 
 =head2 type_constraints_equals
 
-Checks to see if the internal type contraints are equal.
+Checks to see if the internal type constraints are equal.
 
 =cut
 
@@ -4,13 +4,14 @@ use 5.008;
 
 use Moose::Util::TypeConstraints;
 use MooseX::Meta::TypeConstraint::Structured;
+use MooseX::Meta::TypeConstraint::Structured::Optional;
 use MooseX::Types::Structured::OverflowHandler;
-use MooseX::Types -declare => [qw(Dict Tuple Optional)];
-use Sub::Exporter -setup => { exports => [ qw(Dict Tuple Optional slurpy) ] };
+use MooseX::Types -declare => [qw(Dict Map Tuple Optional)];
+use Sub::Exporter -setup => [ qw(Dict Map Tuple Optional slurpy) ];
 use Devel::PartialDump;
 use Scalar::Util qw(blessed);
 
-our $VERSION = '0.19';
+our $VERSION = '0.21';
 our $AUTHORITY = 'cpan:JJNAPIORK';
 
 =head1 NAME
@@ -22,7 +23,7 @@ MooseX::Types::Structured - Structured Type Constraints for Moose
 The following is example usage for this module.
 
     package Person;
-	
+
     use Moose;
     use MooseX::Types::Moose qw(Str Int HashRef);
     use MooseX::Types::Structured qw(Dict Tuple Optional);
@@ -35,7 +36,7 @@ The following is example usage for this module.
             middle => Optional[Str],
         ],
     );
-    
+
     ## description is a string field followed by a HashRef of tagged data.
     has description => (
       isa=>Tuple[
@@ -76,30 +77,30 @@ But all of these would cause a constraint error for the 'name' attribute:
 
     ## Value for 'name' not a HashRef
     Person->new( name => 'John' );
-    
+
     ## Value for 'name' has incorrect hash key and missing required keys
     Person->new( name => {
         first_name => 'John'
     });
-    
+
     ## Also incorrect keys
     Person->new( name => {
         first_name => 'John',
         age => 39,
     });
-    
+
     ## key 'middle' incorrect type, should be a Str not a ArrayRef
     Person->new( name => {
         first => 'Vanessa',
         middle => [1,2],
         last => 'Li',
-    }); 
+    });
 
 And these would cause a constraint error for the 'description' attribute:
 
     ## Should be an ArrayRef
     Person->new( description => 'Hello I am a String' );
-    
+
     ## First element must be a string not a HashRef.
     Person->new (description => [{
         tag1 => 'value1',
@@ -117,7 +118,7 @@ generalized form is:
 
     TypeConstraint[@TypeParameters or %TypeParameters]
 
-Where 'TypeParameters' is an array reference or hash references of 
+Where 'TypeParameters' is an array reference or hash references of
 L<Moose::Meta::TypeConstraint> objects.
 
 This type library enables structured type constraints. It is built on top of the
@@ -127,12 +128,12 @@ if you are not familiar with it.
 =head2 Comparing Parameterized types to Structured types
 
 Parameterized constraints are built into core Moose and you are probably already
-familar with the type constraints 'HashRef' and 'ArrayRef'.  Structured types
+familiar with the type constraints 'HashRef' and 'ArrayRef'.  Structured types
 have similar functionality, so their syntax is likewise similar. For example,
 you could define a parameterized constraint like:
 
     subtype ArrayOfInts,
-     as Arrayref[Int];
+     as ArrayRef[Int];
 
 which would constrain a value to something like [1,2,3,...] and so on.  On the
 other hand, a structured type constraint explicitly names all it's allowed
@@ -140,7 +141,7 @@ other hand, a structured type constraint explicitly names all it's allowed
 
     subtype StringFollowedByInt,
      as Tuple[Str,Int];
-	
+
 would constrain it's value to things like ['hello', 111] but ['hello', 'world']
 would fail, as well as ['hello', 111, 'world'] and so on.  Here's another
 example:
@@ -156,20 +157,20 @@ example:
         Str, Int,
         Optional[HashRef]
      ];
-     
+
 This defines a type constraint that validates values like:
 
     ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
     ['World', 200];
-    
+
 Notice that the last type constraint in the structure is optional.  This is
 enabled via the helper Optional type constraint, which is a variation of the
 core Moose type constraint 'Maybe'.  The main difference is that Optional type
-constraints are required to validate if they exist, while 'Maybe' permits 
+constraints are required to validate if they exist, while 'Maybe' permits
 undefined values.  So the following example would not validate:
 
     StringIntOptionalHashRef->validate(['Hello Undefined', 1000, undef]);
-    
+
 Please note the subtle difference between undefined and null.  If you wish to
 allow both null and undefined, you should use the core Moose 'Maybe' type
 constraint instead:
@@ -188,7 +189,7 @@ constraint instead:
 This would validate the following:
 
     ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
-    ['World', 200, undef];    
+    ['World', 200, undef];
     ['World', 200];
 
 Structured constraints are not limited to arrays.  You can define a structure
@@ -203,17 +204,17 @@ against a HashRef with the 'Dict' type constaint as in this example:
 This would constrain a HashRef that validates something like:
 
     {firstname => 'Christopher', lastname => 'Parsons'};
-    
+
 but all the following would fail validation:
 
     ## Incorrect keys
     {first => 'Christopher', last => 'Parsons'};
-    
+
     ## Too many keys
     {firstname => 'Christopher', lastname => 'Parsons', middlename => 'Allen'};
-    
+
     ## Not a HashRef
-    ['Christopher', 'Parsons']; 
+    ['Christopher', 'Parsons'];
 
 These structures can be as simple or elaborate as you wish.  You can even
 combine various structured, parameterized and simple constraints all together:
@@ -224,13 +225,13 @@ combine various structured, parameterized and simple constraints all together:
         Dict[name=>Str, age=>Int],
         ArrayRef[Int]
      ];
-	
+
 Which would match:
 
 	[1, {name=>'John', age=>25},[10,11,12]];
 
 Please notice how the type parameters can be visually arranged to your liking
-and to improve the clarity of your meaning.  You don't need to run then 
+and to improve the clarity of your meaning.  You don't need to run then
 altogether onto a single line.  Additionally, since the 'Dict' type constraint
 defines a hash constraint, the key order is not meaningful.  For example:
 
@@ -256,34 +257,34 @@ example:
 
     package MyApp::MyStruct;
     use Moose;
-    
+
     ## lazy way to make a bunch of attributes
     has $_ for qw(full_name age_in_years);
-    
+
     package MyApp::MyClass;
     use Moose;
-    
-    has person => (isa => 'MyApp::MyStruct');		
-    
+
+    has person => (isa => 'MyApp::MyStruct');
+
     my $instance = MyApp::MyClass->new(
         person=>MyApp::MyStruct->new(
             full_name => 'John',
             age_in_years => 39,
         ),
     );
-	
+
 This method may take some additional time to setup but will give you more
 flexibility.  However, structured constraints are highly compatible with this
 method, granting some interesting possibilities for coercion.  Try:
 
     package MyApp::MyClass;
-    
+
     use Moose;
     use MyApp::MyStruct;
-    
+
     ## It's recommended your type declarations live in a separate class in order
     ## to promote reusability and clarity.  Inlined here for brevity.
-    
+
     use MooseX::Types::DateTime qw(DateTime);
     use MooseX::Types -declare [qw(MyStruct)];
     use MooseX::Types::Moose qw(Str Int);
@@ -296,7 +297,7 @@ method, granting some interesting possibilities for coercion.  Try:
     ## Just a shorter version really.
     subtype MyStruct,
      as 'MyApp::MyStruct';
-    
+
     ## Add the coercions.
     coerce MyStruct,
      from Dict[
@@ -312,22 +313,22 @@ method, granting some interesting possibilities for coercion.  Try:
      ], via {
         my $name = $_->{firstname} .' '. $_->{lastname};
         my $age = DateTime->now - $_->{dob};
-        
+
         MyApp::MyStruct->new(
             full_name=>$name,
             age_in_years=>$age->years,
         );
      };
-     
-    has person => (isa=>MyStruct);	
-     
+
+    has person => (isa=>MyStruct);
+
 This would allow you to instantiate with something like:
 
     my $obj = MyApp::MyClass->new( person => {
         full_name=>'John Napiorkowski',
         age_in_years=>39,
     });
-    
+
 Or even:
 
     my $obj = MyApp::MyClass->new( person => {
@@ -347,13 +348,13 @@ this example:
 
     subtype Person,
      as Dict[name => Str];
-	 
+
     subtype FriendlyPerson,
      as Person[
         name => Str,
         total_friends => Int,
      ];
-	 
+
 This will actually work BUT you have to take care that the subtype has a
 structure that does not contradict the structure of it's parent.  For now the
 above works, but I will clarify the syntax for this at a future point, so
@@ -371,13 +372,13 @@ Coercions currently work for 'one level' deep.  That is you can do:
         name => Str,
         age => Int
     ];
-    
+
     subtype Fullname,
      as Dict[
         first => Str,
         last => Str
      ];
-    
+
     coerce Person,
      ## Coerce an object of a particular class
      from BlessedPersonObject, via {
@@ -386,7 +387,7 @@ Coercions currently work for 'one level' deep.  That is you can do:
             age=>$_->age,
         };
      },
-     
+
      ## Coerce from [$name, $age]
      from ArrayRef, via {
         +{
@@ -404,7 +405,7 @@ Coercions currently work for 'one level' deep.  That is you can do:
             age =>$age->years
         }
      };
-	 
+
 And that should just work as expected.  However, if there are any 'inner'
 coercions, such as a coercion on 'Fullname' or on 'DateTime', that coercion
 won't currently get activated.
@@ -426,7 +427,7 @@ example:
 	 		ArrayRef[Person]
 	 	],
 	 ];
-	 
+
 This would declare a Person subtype that contains a name and an optional
 ArrayRef of Persons who are friends as in:
 
@@ -453,7 +454,7 @@ a Union with an non recursive option such as:
 	 	Str,
 	 	Str|Tuple,
 	 ];
-	 
+
 Which validates:
 
 	[
@@ -467,7 +468,7 @@ Which validates:
 		],
 	];
 
-Otherwise you will define a subtype thatis impossible to validate since it is 
+Otherwise you will define a subtype thatis impossible to validate since it is
 infinitely recursive.  For more information about defining recursive types,
 please see the documentation in L<MooseX::Types> and the test cases.
 
@@ -486,7 +487,7 @@ list of contained constraints.  For example:
 The Values of @constraints should ideally be L<MooseX::Types> declared type
 constraints.  We do support 'old style' L<Moose> string based constraints to a
 limited degree but these string type constraints are considered deprecated.
-There will be limited support for bugs resulting from mixing string and 
+There will be limited support for bugs resulting from mixing string and
 L<MooseX::Types> in your structures.  If you encounter such a bug and really
 need it fixed, we will required a detailed test case at the minimum.
 
@@ -497,9 +498,20 @@ hashref.  For example:
 
     Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
 
-The keys in %constraints follow the same rules as @constraints in the above 
+The keys in %constraints follow the same rules as @constraints in the above
 section.
 
+=head2 Map[ $key_constraint, $value_constraint ]
+
+This defines a HashRef based constraint in which both the keys and values are
+required to meet certain constraints.  For example, to map hostnames to IP
+addresses, you might say:
+
+  Map[ HostName, IPAddress ]
+
+The type constraint would only be met if every key was a valid HostName and
+every value was a valid IPAddress.
+
 =head2 Optional[$constraint]
 
 This is primarily a helper constraint for Dict and Tuple type constraints.  What
@@ -514,7 +526,7 @@ or a tuple where some of the values are not required.  For example:
         last=>Str,
         middle=>Optional[Str],
     ];
-        
+
 Creates a constraint that validates against a hashref with the keys 'first' and
 'last' being strings and required while an optional key 'middle' is must be a
 string if it appears but doesn't have to appear.  So in this case both the
@@ -556,7 +568,7 @@ In order to allow structured validation of, "and then some", arguments, you can
 use the L</slurpy> method against a type constraint.  For example:
 
     use MooseX::Types::Structured qw(Tuple slurpy);
-    
+
     subtype AllowTailingArgs,
      as Tuple[
        Int,
@@ -568,7 +580,7 @@ use the L</slurpy> method against a type constraint.  For example:
 This will now work as expected, validating ArrayRef structures such as:
 
     [1,"hello", $obj, 2,3,4,5,6,...]
-    
+
 A few caveats apply.  First, the slurpy type constraint must be the last one in
 the list of type constraint parameters.  Second, the parent type of the slurpy
 type constraint must match that of the containing type constraint.  That means
@@ -612,25 +624,25 @@ constraint and coercions.  This example also shows structured types mixed which
 other MooseX::Types libraries.
 
     package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
-    
+
     use Moose;
     use DateTime;
-    
+
     use MooseX::Types::Structured qw(Dict Tuple);
     use MooseX::Types::DateTime qw(DateTime);
     use MooseX::Types::Moose qw(Int Str Object);
     use MooseX::Types -declare => [qw(Name Age Person)];
-     
+
     subtype Person,
      as Dict[
      	name=>Str,
      	age=>Int,
      ];
-    
+
     coerce Person,
      from Dict[
-     	first=>Str, 
-     	last=>Str, 
+     	first=>Str,
+     	last=>Str,
      	years=>Int,
      ], via { +{
         name => "$_->{first} $_->{last}",
@@ -638,9 +650,9 @@ other MooseX::Types libraries.
      }},
      from Dict[
      	fullname=>Dict[
-     		last=>Str, 
+     		last=>Str,
      		first=>Str,
-     	], 
+     	],
      	dob=>DateTime,
      ],
      ## DateTime needs to be inside of single quotes here to disambiguate the
@@ -650,18 +662,18 @@ other MooseX::Types libraries.
         name => "$_->{fullname}{first} $_->{fullname}{last}",
         age => ($_->{dob} - 'DateTime'->now)->years,
      }};
-     
+
     has person => (is=>'rw', isa=>Person, coerce=>1);
-    
+
 And now you can instantiate with all the following:
 
     __PACKAGE__->new(
         person=>{
             name=>'John Napiorkowski',
-            age=>39,            
+            age=>39,
         },
     );
-        
+
     __PACKAGE__->new(
         person=>{
             first=>'John',
@@ -669,7 +681,7 @@ And now you can instantiate with all the following:
             years=>39,
         },
     );
-    
+
     __PACKAGE__->new(
         person=>{
             fullname => {
@@ -680,31 +692,57 @@ And now you can instantiate with all the following:
                 year=>1969,
                 month=>2,
                 day=>13
-            ),            
+            ),
         },
     );
-    
+
 This technique is a way to support various ways to instantiate your class in a
 clean and declarative way.
 
 =cut
 
+my $Optional = MooseX::Meta::TypeConstraint::Structured::Optional->new(
+    name => 'MooseX::Types::Structured::Optional',
+    package_defined_in => __PACKAGE__,
+    parent => find_type_constraint('Item'),
+    constraint => sub { 1 },
+    constraint_generator => sub {
+        my ($type_parameter, @args) = @_;
+        my $check = $type_parameter->_compiled_type_constraint();
+        return sub {
+            my (@args) = @_;
+            ## Does the arg exist?  Something exists if it's a 'real' value
+            ## or if it is set to undef.
+            if(exists($args[0])) {
+                ## If it exists, we need to validate it
+                $check->($args[0]);
+            } else {
+                ## But it's is okay if the value doesn't exists
+                return 1;
+            }
+        }
+    }
+);
+
+Moose::Util::TypeConstraints::register_type_constraint($Optional);
+Moose::Util::TypeConstraints::add_parameterizable_type($Optional);
+
 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
 	MooseX::Meta::TypeConstraint::Structured->new(
 		name => "MooseX::Types::Structured::Tuple" ,
 		parent => find_type_constraint('ArrayRef'),
-		constraint_generator=> sub { 
+		constraint_generator=> sub {
 			## Get the constraints and values to check
             my ($type_constraints, $values) = @_;
 			my @type_constraints = defined $type_constraints ?
              @$type_constraints : ();
-            
+
             my $overflow_handler;
             if($type_constraints[-1] && blessed $type_constraints[-1]
               && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) {
                 $overflow_handler = pop @type_constraints;
             }
-            
+
 			my @values = defined $values ? @$values: ();
 			## Perform the checking
 			while(@type_constraints) {
@@ -715,10 +753,10 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
                         $_[2]->{message} = $type_constraint->get_message($value)
                          if ref $_[2];
 						return;
-					}				
+					}
 				} else {
                     ## Test if the TC supports null values
-					unless($type_constraint->check()) {
+                    unless ($type_constraint->is_subtype_of($Optional)) {
                         $_[2]->{message} = $type_constraint->get_message('NULL')
                          if ref $_[2];
 						return;
@@ -745,22 +783,22 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
 		}
 	)
 );
-	
+
 Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
 	MooseX::Meta::TypeConstraint::Structured->new(
 		name => "MooseX::Types::Structured::Dict",
 		parent => find_type_constraint('HashRef'),
-		constraint_generator=> sub { 
+		constraint_generator=> sub {
 			## Get the constraints and values to check
             my ($type_constraints, $values) = @_;
 			my @type_constraints = defined $type_constraints ?
              @$type_constraints : ();
-            
+
             my $overflow_handler;
             if($type_constraints[-1] && blessed $type_constraints[-1]
               && $type_constraints[-1]->isa('MooseX::Types::Structured::OverflowHandler')) {
                 $overflow_handler = pop @type_constraints;
-            } 
+            }
             my (%type_constraints) = @type_constraints;
 			my %values = defined $values ? %$values: ();
 			## Perform the checking
@@ -777,7 +815,7 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
 					}
 				} else {
                     ## Test to see if the TC supports null values
-					unless($type_constraint->check()) {
+                    unless ($type_constraint->is_subtype_of($Optional)) {
                         $_[2]->{message} = $type_constraint->get_message('NULL')
                          if ref $_[2];
 						return;
@@ -785,7 +823,7 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
 				}
 			}
 			## Make sure there are no leftovers.
-			if(%values) { 
+			if(%values) {
                 if($overflow_handler) {
                     return $overflow_handler->check(+{%values});
                 } else {
@@ -805,33 +843,45 @@ Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
 	)
 );
 
-OPTIONAL: {
-    my $Optional = Moose::Meta::TypeConstraint::Parameterizable->new(
-        name => 'MooseX::Types::Structured::Optional',
-        package_defined_in => __PACKAGE__,
-        parent => find_type_constraint('Item'),
-        constraint => sub { 1 },
-        constraint_generator => sub {
-            my ($type_parameter, @args) = @_;
-            my $check = $type_parameter->_compiled_type_constraint();
-            return sub {
-                my (@args) = @_;
-                ## Does the arg exist?  Something exists if it's a 'real' value
-                ## or if it is set to undef.
-                if(exists($args[0])) {
-                    ## If it exists, we need to validate it
-                    $check->($args[0]);
-                } else {
-                    ## But it's is okay if the value doesn't exists
-                    return 1;
-                }
-            }
+Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
+  MooseX::Meta::TypeConstraint::Structured->new(
+    name => "MooseX::Types::Structured::Map",
+    parent => find_type_constraint('HashRef'),
+    constraint_generator=> sub {
+      ## Get the constraints and values to check
+      my ($type_constraints, $values) = @_;
+      my @constraints = defined $type_constraints ? @$type_constraints : ();
+
+      Carp::confess( "too many args for Map type" ) if @constraints > 2;
+
+      my ($key_type, $value_type) = @constraints == 2 ? @constraints
+                                  : @constraints == 1 ? (undef, @constraints)
+                                  :                     ();
+
+      my %values = defined $values ? %$values: ();
+      ## Perform the checking
+      if ($value_type) {
+        for my $value (values %$values) {
+          unless ($value_type->check($value)) {
+            $_[2]->{message} = $value_type->get_message($value) if ref $_[2];
+            return;
+          }
         }
-    );
+      }
+
+      if ($key_type) {
+        for my $key (keys %$values) {
+          unless ($key_type->check($key)) {
+            $_[2]->{message} = $key_type->get_message($key) if ref $_[2];
+            return;
+          }
+        }
+      }
 
-    Moose::Util::TypeConstraints::register_type_constraint($Optional);
-    Moose::Util::TypeConstraints::add_parameterizable_type($Optional);
-}
+      return 1;
+    },
+  )
+);
 
 sub slurpy ($) {
 	my ($tc) = @_;
@@ -854,7 +904,7 @@ Here's a list of stuff I would be happy to get volunteers helping with:
 	* All POD examples need test cases in t/documentation/*.t
 	* Want to break out the examples section to a separate cookbook style POD.
 	* Want more examples and best practice / usage guidance for authors
-	* Need to clarify deep coercions, 
+	* Need to clarify deep coercions,
 
 =head1 AUTHOR
 
@@ -877,5 +927,5 @@ This program is free software; you can redistribute it and/or modify it under
 the same terms as Perl itself.
 
 =cut
-	
+
 1;
@@ -0,0 +1,21 @@
+use strict;
+use warnings;
+use Test::More;
+
+use MooseX::Types::Moose qw(Int Num);
+use MooseX::Types::Structured qw(Map);
+
+my $type = Map[ Int, Num ];
+
+ok($type->assert_valid({ 10 => 10.5 }), "simple Int -> Num mapping");
+
+eval { $type->assert_valid({ 10.5 => 10.5 }) };
+like($@, qr{value 10\.5}, "non-Int causes rejection on key");
+
+eval { $type->assert_valid({ 10 => "ten and a half" }) };
+like("$@", qr{value ten and a half}, "non-Num value causes rejection on value");
+
+ok($type->assert_valid({ }), "empty hashref is a valid mapping of any sort");
+
+done_testing;
+
@@ -0,0 +1,43 @@
+use strict;
+use warnings;
+use Test::More;
+
+BEGIN {
+    package MyTypes;
+    use MooseX::Types::Structured qw(Dict Tuple Optional);
+    use MooseX::Types::Moose qw(Object Any);
+    use MooseX::Types -declare => [qw(
+        Signature
+        MyDict
+        MyTuple
+    )];
+
+    subtype Signature, as Tuple[Tuple[Object], Dict[optional => Optional[Any], required => Any]];
+
+    subtype MyDict, as Dict[optional => Optional[Any], required => Any];
+
+    subtype MyTuple, as Tuple[Object, Any, Optional[Any]];
+}
+
+BEGIN {
+    MyTypes->import(':all');
+}
+
+ok(!Signature->check([ [bless {}, 'Foo'], {} ]));
+
+ok(!MyDict->check({ }));
+ok(!MyDict->check({ optional => 42 }));
+ok(!MyDict->check({ optional => 42, unknown => 23 }));
+ok(!MyDict->check({ required => 42, unknown => 23 }));
+
+ok(MyDict->check({ optional => 42, required => 23 }));
+ok(MyDict->check({ required => 23 }));
+
+ok(!MyTuple->check([]));
+ok(!MyTuple->check([bless({}, 'Foo')]));
+ok(!MyTuple->check([bless({}, 'Foo'), 'bar', 'baz', 'moo']));
+
+ok(MyTuple->check([bless({}, 'Foo'), 'bar']));
+ok(MyTuple->check([bless({}, 'Foo'), 'bar', 'baz']));
+
+done_testing;